package com.hs.sharding.algorithm;

import com.google.common.base.Preconditions;
import com.google.common.collect.Range;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;


import java.util.*;

/**
 * 自定义分片策略 , 我们这里实现标准的分片算法接口StandardShardingAlgorithm
 * 我这里是分片逻辑就是按照个数取模，在分发到sys_user1   sys_user2数据表中
 */
public class HsComplexAlgorithm implements StandardShardingAlgorithm<Long> {

    /**
     * 数据库个数
     */
    private final String DB_COUNT = "db-count";
    /**
     * 数据表个数
     */
    private final String TAB_COUNT = "tab-count";
    /**
     * 真实数据表前缀
     */
    private final String PERTAB = "pertab";


    private Integer dbCount;
    private Integer tabCount;
    private String pertab;

    private Properties props;

    @Override
    public void init(Properties props) {
        this.props = props;
        this.dbCount = getDbCount(props);
        this.tabCount = getTabCount(props);
        this.pertab = getPertab(props);
        // 校验条件
        Preconditions.checkState(null != pertab && !pertab.isEmpty(),
                "Inline hsComplex algorithm expression cannot be null or empty.");
    }

    /**
     * 精确查询分片执行接口（对应的sql是where ??=值)
     * @param collection 可用的分片名集合(分库就是库名，分表就是表名)
     * @param preciseShardingValue 分片键
     */
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {

        Long uid = preciseShardingValue.getValue();
        String resultTableName = pertab + ((uid + 1) % (dbCount * tabCount) / tabCount + 1);
        if (collection.contains(resultTableName)){
            return resultTableName;
        }
        throw new UnsupportedOperationException("route: " + resultTableName + " is not supported, please check your config");
    }

    /**
     * 范围分片规则（对应的是where ??>='XXX' and ??<='XXX')
     * 范围查询分片算法（分片键涉及区间查询时会进入该方法进行分片计算）
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
        List<String> result = new ArrayList<>();
        Range<Long> valueRange = rangeShardingValue.getValueRange();
        Long upperEndpoint = valueRange.upperEndpoint();
        Long aLong = valueRange.lowerEndpoint();

        // TODO 进行相应的分片判断
//        return result;

        return collection;
    }

    private String getPertab(Properties props) {
        return props.getProperty(PERTAB);
    }

    private Integer getDbCount(Properties props) {
        String count = props.getProperty(DB_COUNT);
        return count == null || count.isEmpty() ? 0 : Integer.valueOf(count);
    }

    private Integer getTabCount(Properties props) {
        String count = props.getProperty(TAB_COUNT);
        return count == null || count.isEmpty() ? 0 : Integer.valueOf(count);
    }


    @Override
    public Properties getProps() {
        return props;
    }

    @Override
    public String getType() {
        return "HS";
    }
}
